#pragma once

#include "Thread.hpp"
#include "Mutex.hpp"
#include "log.hpp"

const int g_thread_num = 3; // 线程池的个数

template<class T>
class ThreadPool
{
public:
    pthread_mutex_t* GetMutex()
    {
        return &_mtx;
    }

    bool IsEmpty()
    {
        return task_queue.empty();
    }

    void wait()
    {
        pthread_cond_wait(&_cond, &_mtx);
    }

    T GetTask()  // 设计接口来服务线程的回调函数
    {
        T t = task_queue.front(); // 拿取任务
        task_queue.pop(); // 删除任务
        return t; // 返回任务
    }
private:
    ThreadPool(int thread_num = g_thread_num)
        :_num(thread_num)
    {
        pthread_mutex_init(&_mtx, nullptr);  // 锁初始化
        pthread_cond_init(&_cond, nullptr);  // 条件变量初始化

        for (int i = 0; i <= _num; i++)
        {
            _threads.push_back(new Thread(i, rountine, this));
        }
    }

    ThreadPool(const ThreadPool<T>& other) = delete;
    const ThreadPool<T>& operator=(const ThreadPool<T>& other) = delete;
public:
    static ThreadPool<T>* getThreadPool(int num = g_thread_num)
    {
        if (nullptr == thread_ptr)
        {
            RAII_Mutex raii_mutex(&mutex);
            if (thread_ptr == nullptr)
                thread_ptr = new ThreadPool<T>(num);   // 这里我直接创建了
        }
        return thread_ptr;
    }

    void run()  // 让线程池运行起来, 即就是让线程各自执行自己的执行方法,线程池的构造中只是开辟了线程的空间,还没有创建线程
    {
        for (auto& iter : _threads)
        {
            iter->start();  // 创建线程,创建了线程,线程就会自动执行自己的回调函数
            logMessage(NORMAL, "%s %s", iter->name().c_str(), "启动成功!");  // 线程的名字 启动成功
        }
    }

    // 类中的静态成员是无法访问私有成员的,如果非要访问,就设计一些它要用的接口,或者不要把该函数写在类内
    static void* rountine(void* args)  // 这里线程池的线程只担任消费者角色,拿任务,处理任务
    {
        ThreadData* td = (ThreadData*) args;
        ThreadPool<T>* tp = (ThreadPool<T>*)td->_args;

        while (true)
        {
            T task; // 定义一个任务 -> 拿取任务
            {  // 这里是拿取任务的部分,属于临界区,使用一个代码块
                RAII_Mutex raii_mutex(tp->GetMutex()); // 这里通过设计接口实现上锁
                while (tp->IsEmpty()) tp->wait();  // 如果队列为空,没有任务,那就等待

                task = tp->GetTask();
            }
            task(td->_name); // 执行任务, Task.hpp中需要线程的名称
        }
    }

    void PushTask(const T& t)  // 线程池来充当生产者的校色,设计一个接口来推送任务
    {
        RAII_Mutex raii_mutex(&_mtx); // 上锁
        task_queue.push(t); // 推送任务
        pthread_cond_signal(&_cond); // 唤醒你拿取任务

        // 这里还是一个串行执行的,如果想要并行执行,设计两把锁,让生产者和消费者各自使用自己的锁,
        //  这里的阻塞队列task_queue也没有上限,生产者一直在生产,也不是很好
        // 可以使用环形队列的思路,采用信号量 P V 操作来替代条件变量,实现并发处理
    }

    ~ThreadPool()
    {
        for (auto& iter : _threads)  // iter就是线程的指针,在Thread.hpp文件中已经封装好了,使用指针直接调用接口即可
        {
            iter->join();
            delete iter;
        }
        pthread_mutex_destroy(&_mtx);
        pthread_cond_destroy(&_cond);
    }
private:
    std::vector<Thread*> _threads; // 定义线程池
    int _num;  // 线程池中线程的个数
    std::queue<T> task_queue; // 任务队列

    static ThreadPool<T>* thread_ptr;
    static pthread_mutex_t mutex;

    pthread_mutex_t _mtx; // 定义锁
    pthread_cond_t _cond; // 定义条件变量
};

template<typename T>
ThreadPool<T>* ThreadPool<T>::thread_ptr = nullptr;

template<typename T>
pthread_mutex_t ThreadPool<T>::mutex = PTHREAD_MUTEX_INITIALIZER;
